/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 * Class: mxConnector
 *
 * Extends <mxShape> to implement a connector shape. The connector
 * shape allows for arrow heads on either side.
 *
 * This shape is registered under <mxConstants.SHAPE_CONNECTOR> in
 * <mxCellRenderer>.
 *
 * Constructor: mxConnector
 *
 * Constructs a new connector shape.
 *
 * Parameters:
 *
 * points - Array of <mxPoints> that define the points. This is stored in
 * <mxShape.points>.
 * stroke - String that defines the stroke color. This is stored in <stroke>.
 * Default is 'black'.
 * strokewidth - Optional integer that defines the stroke width. Default is
 * 1. This is stored in <strokewidth>.
 */
function mxConnector(points, stroke, strokewidth) {
  mxPolyline.call(this, points, stroke, strokewidth);
}

/**
 * Extends mxPolyline.
 */
mxUtils.extend(mxConnector, mxPolyline);

/**
 * Function: updateBoundingBox
 *
 * Updates the <boundingBox> for this shape using <createBoundingBox> and
 * <augmentBoundingBox> and stores the result in <boundingBox>.
 */
mxConnector.prototype.updateBoundingBox = function () {
  this.useSvgBoundingBox =
    this.style != null && this.style[mxConstants.STYLE_CURVED] == 1;
  mxShape.prototype.updateBoundingBox.apply(this, arguments);
};

/**
 * Function: paintEdgeShape
 *
 * Paints the line shape.
 */
mxConnector.prototype.paintEdgeShape = function (c, pts) {
  // The indirection via functions for markers is needed in
  // order to apply the offsets before painting the line and
  // paint the markers after painting the line.
  var sourceMarker = this.createMarker(c, pts, true);
  var targetMarker = this.createMarker(c, pts, false);

  mxPolyline.prototype.paintEdgeShape.apply(this, arguments);

  // Disables shadows, dashed styles and fixes fill color for markers
  c.setFillColor(this.stroke);
  c.setShadow(false);
  c.setDashed(false);

  if (sourceMarker != null) {
    sourceMarker();
  }

  if (targetMarker != null) {
    targetMarker();
  }
};

/**
 * Function: createMarker
 *
 * Prepares the marker by adding offsets in pts and returning a function to
 * paint the marker.
 */
mxConnector.prototype.createMarker = function (c, pts, source) {
  var result = null;
  var n = pts.length;
  var type = mxUtils.getValue(
    this.style,
    source ? mxConstants.STYLE_STARTARROW : mxConstants.STYLE_ENDARROW
  );
  var p0 = source ? pts[1] : pts[n - 2];
  var pe = source ? pts[0] : pts[n - 1];

  if (type != null && p0 != null && pe != null) {
    var count = 1;

    // Uses next non-overlapping point
    while (
      count < n - 1 &&
      Math.round(p0.x - pe.x) == 0 &&
      Math.round(p0.y - pe.y) == 0
    ) {
      p0 = source ? pts[1 + count] : pts[n - 2 - count];
      count++;
    }

    // Computes the norm and the inverse norm
    var dx = pe.x - p0.x;
    var dy = pe.y - p0.y;

    var dist = Math.max(1, Math.sqrt(dx * dx + dy * dy));

    var unitX = dx / dist;
    var unitY = dy / dist;

    var size = mxUtils.getNumber(
      this.style,
      source ? mxConstants.STYLE_STARTSIZE : mxConstants.STYLE_ENDSIZE,
      mxConstants.DEFAULT_MARKERSIZE
    );

    // Allow for stroke width in the end point used and the
    // orthogonal vectors describing the direction of the marker
    var filled =
      this.style[
        source ? mxConstants.STYLE_STARTFILL : mxConstants.STYLE_ENDFILL
      ] != 0;

    result = mxMarker.createMarker(
      c,
      this,
      type,
      pe,
      unitX,
      unitY,
      size,
      source,
      this.strokewidth,
      filled
    );
  }

  return result;
};

/**
 * Function: augmentBoundingBox
 *
 * Augments the bounding box with the strokewidth and shadow offsets.
 */
mxConnector.prototype.augmentBoundingBox = function (bbox) {
  mxShape.prototype.augmentBoundingBox.apply(this, arguments);

  // Adds marker sizes
  var size = 0;

  if (
    mxUtils.getValue(
      this.style,
      mxConstants.STYLE_STARTARROW,
      mxConstants.NONE
    ) != mxConstants.NONE
  ) {
    size =
      mxUtils.getNumber(
        this.style,
        mxConstants.STYLE_STARTSIZE,
        mxConstants.DEFAULT_MARKERSIZE
      ) + 1;
  }

  if (
    mxUtils.getValue(
      this.style,
      mxConstants.STYLE_ENDARROW,
      mxConstants.NONE
    ) != mxConstants.NONE
  ) {
    size =
      Math.max(
        size,
        mxUtils.getNumber(
          this.style,
          mxConstants.STYLE_ENDSIZE,
          mxConstants.DEFAULT_MARKERSIZE
        )
      ) + 1;
  }

  bbox.grow(size * this.scale);
};
